<?php
/**
 * @package qBase
 * @subpackage web
 *
 */

/**
 * The QWebControl class is the base of any web control
 * 
 * @package qBase
 * @subpackage web
 * 
 */
class QWebControl extends QObject
{
	/**
	 * The default tag for web controls. This may be ignored by objects inheriting QWebControl that have thir own implementation.
	 *
	 */
	const DefaultTag = "div";
	/**
	 * The id separator for web controls IDs. Each control will have their ID build based on parents id. Example: ctrl_1≡ctrl_2≡this_ctrl
	 *
	 */
	const IdSeparator = "-";
	// const IdSeparator = "≡";
	/**
	 * Stores the resource paths of all QWebControl(s) based on their class name
	 *
	 * @var array
	 */
	private static $_ResPaths;
	/**
	 * The parent of this QWebControl
	 *
	 * @var QWebControl
	 */
	private $_parent;
	/**
	 * The child controls for this QWebControl
	 *
	 * @var array
	 */
	private $_controls;
	/**
	 * The child controls index stores the controls on (ID => control) pattern
	 *
	 * @var integer
	 */
	private $_controls_index;
	/**
	 * All the events are stored here based on event type ("event type" => QEvent object)
	 *
	 * @var array
	 */
	private $_events = null;
	/**
	 * The template of this QWebControl, it's not necessary some controls can render without
	 *
	 * @var string
	 */
	private $_template;
	/**
	 * The html of this QWebControl, it's not necessary controls can render without
	 * If specified will overrite the template
	 *
	 * @var string
	 */
	private $_inner_content;
	/**
	 * The name of the Javascript class associated to this QWebControl
	 *
	 * @var string
	 */
	private $_js_class;
	/**
	 * The resource path for this web control
	 *
	 * @var string
	 */
	private $_res_path;
	/**
	 * 	The tag name of this web control
	 *
	 * @var string
	 */
	private $_tag_name = QWebControl::DefaultTag;
	/**
	 * The attributes of this control specified as key => value
	 *
	 * @var array
	 */
	private $_attributes;
	/**
	 * The id of the control within it's container
	 *
	 * @var string
	 */
	private $_id;
	/**
	 * The fullId of the control within it's container
	 *
	 * @var string
	 */
	private $_fullId;
	/**
	 * Marks that the current control has been changed and that it should be resent to the browser (usually on ajax request)
	 *
	 * @var boolean
	 */
	private $_has_changed = false;
	/**
	 * The viewstate of this web control. Variables may be set by a key => value pattern. Keys starting with '_' are reserved.
	 *
	 * @var array
	 */
	protected $_viewstate;
	/**
	 * If true will use the default res path as set on QWebApp::$DefaultResPath
	 *
	 * @var boolean
	 */
	private $_use_default_res_path = false;
	/**
	 * An associative array that will be shared with the javascript object equivalent to this object
	 * Only place Scalar values in this array !
	 *
	 * @var array
	 */
	private $_js_shared_params;
	/**
	 * True to send the JS params
	 *
	 * @var boolean
	 */
	private $_js_shared_params_set = false;
	/**
	 * The resources of the control that the web page should include
	 *
	 * @var array
	 */
	protected $_resources;
	/**
	 * The render callback delegate
	 *
	 * @var QDelegate
	 */
	protected $_render_delegate;
	/**
	 * The list of properties as strings in array that should be saved and restored 
	 * in the viewstate automatically
	 *
	 * @var array
	 */
	protected $_viewstate_managed_properties;
	/**
	 * The list of properties (strings in array) to be sync with the javascript object
	 * THE WAY BACK IS NOT IMPLEMENTED YET, VALUES WILL NOT BE SENT BACK FROM JAVASCRIPT TO PHP !!!
	 *
	 * @var array
	 */
	protected $_js_sync_properties;
	/**
	 * The constructor of QWebControl
	 *
	 */
	public function __construct($id = null)
	{
		parent::__construct();
		
		if ($id)
		{
			if (strpos($id, self::IdSeparator) !== false)
				throw new Exception("The ID of a QWebControl can not contain the `".self::IdSeparator."` character");
			$this->setId($id);
		}
	}
	
	/**
	 * This function is called to initialize the control. Each inheriting class should override this method.
	 *
	 */
	protected function init()
	{
		// echo "init called";
	}
	
	public function beforeInit()
	{
		
	}
	
	/**
	 * Triggers the init call to this object and child controls
	 *
	 */
	protected final function doInit()
	{
		$this->beforeInit();
		
		$this->init();

		if ($this->_controls)
		{
			foreach ($this->_controls as $ctrl)
				$ctrl->doInit();
		}
		
		$this->triggerEvent("init", null);
	}
	
	/**
	 * This function is triggered when the control should load data from the viewstate
	 *
	 * @param QWebPage $webpage
	 */
	protected function loadViewstate(QWebPage $webpage)
	{
		
	}
	
	/**
	 * Loads the viewstate and calls for this control and child controls to laod the viewstate
	 *
	 * @param QWebPage $webpage
	 */
	protected final function doLoadViewstate(QWebPage $webpage)
	{
		if (!$this->_viewstate)
			return ;
			
		if (isset($this->_viewstate_managed_properties))
			foreach ($this->_viewstate_managed_properties as $prop)
			{
				if (isset($this->_viewstate[$prop]))
					$this->{$prop} = $this->_viewstate[$prop];
			}
			
		$this->loadViewstate($webpage);

		if ($this->_controls)
		{
			if (isset($this->_viewstate["_ctrls"]))
			{
				foreach ($this->_controls_index as $id => $ctrl)
				{
					if (isset($this->_viewstate["_ctrls"][$id]))
					{
						$ctrl->_viewstate = $this->_viewstate["_ctrls"][$id];
						$ctrl->doLoadViewstate($webpage);
					}
				}
				// unset($this->_viewstate["_ctrls"]);
			}
		}
	}
	
	/**
	 * Triggered when the control should load the postback data
	 *
	 */
	protected function loadPostback()
	{
		
	}
	
	/**
	 * This method triggers the load of the postback to this control and child controls
	 *
	 */
	protected final function doLoadPostback()
	{
		$this->loadPostback();

		if ($this->_controls)
		{
			foreach ($this->_controls as $ctrl)
				$ctrl->doLoadPostback();
		}
	}

	/**
	 * Triggered when the control has loadded
	 *
	 * @param QWebPage $webpage
	 */
	protected function load(QWebPage $webpage = null)
	{
		
	}
	
	protected function beforeLoad(QWebPage $webpage = null)
	{
		
	}
	
	protected function afterLoad(QWebPage $webpage = null)
	{
		
	}
	
	/**
	 * This function triggers the load event to this control and child controls
	 *
	 * @param QWebPage $webpage
	 */
	protected final function doLoad(QWebPage $webpage)
	{
		$this->beforeLoad($webpage);
		$this->load($webpage);

		if ($this->_controls)
		{
			foreach ($this->_controls as $ctrl)
				$ctrl->doLoad($webpage);
		}
		
		$this->triggerEvent("load", null);
		$this->afterLoad($webpage);
	}
	
	protected final function doAfterEvents(QWebPage $webpage)
	{
		$this->afterEvents($webpage);

		if ($this->_controls)
		{
			foreach ($this->_controls as $ctrl)
				$ctrl->doAfterEvents($webpage);
		}
	}
	
	public function afterEvents(QWebPage $webpage = null)
	{
		
	}
	
	protected final function doUnload(QWebPage $webpage)
	{
		$this->unload($webpage);

		if ($this->_controls)
		{
			foreach ($this->_controls as $ctrl)
				$ctrl->doUnload($webpage);
		}
		
		$this->triggerEvent("unload", null);
	}
	
	public function unload(QWebPage $webpage = null)
	{
		
	}
	
	/**
	 * This method is triggered when the control should save it's data to the viewstate
	 *
	 */
	protected function saveViewstate()
	{
		
	}
	
	/**
	 * This method triggers the save viewstate to this control and child controls
	 *
	 */
	protected final function doSaveViewstate()
	{
		if (isset($this->_viewstate["_ctrls"]))
			unset($this->_viewstate["_ctrls"]);
			
		if (isset($this->_viewstate_managed_properties))
			foreach ($this->_viewstate_managed_properties as $prop)
			{
				if (isset($this->{$prop}))
					$this->_viewstate[$prop] = $this->{$prop};
			}

		$this->saveViewstate();

		if ($this->_controls)
		{
			$this->_viewstate["_ctrls"] = array();
			
			foreach ($this->_controls as $ctrl)
			{
				$ctrl->doSaveViewstate();
				$this->_viewstate["_ctrls"][$ctrl->getId()] = $ctrl->_viewstate;
			}
		}
	}

	/**
	 * Creates the javascript stream
	 *
	 * @param QWebPage $webpage
	 * @param string $full_id The control's fullId
	 * @param integer $depth The depth of this rendering step
	 */
	protected final function prepareRender(QWebPage $webpage, $full_id = null, $depth = 0, $parent_changed = false)
	{
		if (!$this->isWebPage())
			$this->setAttribute("id", $full_id);

		// ajax render must be performed on the control
		// normal render 
		// $normal_render = !$webpage->isAjaxRequest();
		// no render is necessary
		// $no_render = $webpage->isAjaxRequest() && (!$ajax_render);

		// include the js equivalent of this object

		$webpage->getClassJsNeededFiles($this->getClassName());
		if (isset($this->_resources))
			$webpage->addControlResources($this->_resources);

		$this->renderJs($webpage, $full_id, $depth, $parent_changed);
		
		if ($this->_controls)
		{
			$child_parent_changed = $this->hasChanged() || $parent_changed;
			foreach ($this->_controls as $ctrl)
				$ctrl->prepareRender($webpage, $ctrl->getFullId($full_id), $depth + 1, $child_parent_changed);
		}

		$this->renderJsEnd($webpage, $full_id, $depth, $parent_changed);
	}
	
	/**
	 * Manages the render of the control in case of an AJAX request
	 * If the control has changed the control is rebuild along with the child controls
	 * If not it passes the request to the child controls 
	 *
	 * @param QWebPage $webpage
	 * @param string $full_id
	 * @param integer $depth
	 */
	protected final function renderAjax(QWebPage $webpage, $full_id = null, $depth = 0)
	{
		// $changed = $this->_has_changed ? "true" : "false";
		// $this->debug("render ({$changed}): " . $this->getFullId());
		// var_dump("renderAjax");
		
		if ($this->_has_changed)
		{
			// echo "this has changed !";
			ob_start();
			// $this->debug("render: " . $this->getTemplate());
			$this->render($webpage, $depth);
			$out = ob_get_clean();
			
			// add chunk to the js stream
			$webpage->addAjaxReplaceChunk($full_id, $out);
		}
		else if ($this->_controls)
		{
			foreach ($this->_controls as $ctrl)
				$ctrl->renderAjax($webpage, $ctrl->getFullId($full_id), $depth + 1);
		}
	}

	/**
	 * Renders the web control. This function may be overridden by implementing classses
	 *
	 * @param QWebPage $webpage
	 * @param integer $depth The depth of this rendering step
	 */
	protected function render(QWebPage $webpage = null, $depth = 0)
	{
		if (isset($this->_render_delegate))
		{
			call_user_func($this->_render_delegate, $webpage, $depth, $this);
			return ;
		}
		
		if (!$this->isWebPage()) echo "<{$this->_tag_name}";
		if (!$this->getAttribute("id"))
			$this->setAttribute("id", $this->getFullId());
		if ($this->_attributes && (count($this->_attributes) > 0) && (!$this->isWebPage()))
		{
			foreach ($this->_attributes as $name => $value)
				echo " {$name}=\"".htmlspecialchars($value)."\"";
		}
		if (!$this->isWebPage()) echo ">";
		
		// render child controls
		if ($this->_inner_content)
		{
			echo $this->_inner_content;
		}
		else if ($this->_template)
		{
			require($this->_template);
		}
		else if ($this->_controls)
		{
			foreach ($this->_controls as $ctrl)
				$ctrl->render($webpage, $depth + 1);
		}
		
		if (!$this->isWebPage()) echo "</{$this->_tag_name}>";
	}

	/**
	 * NOTE: We should map resources in the future to gain speed
	 * 
	 * Gets the full path of a resource file that is relatively specified to the control's resource folder
	 * for example : 
	 * $web_page = new QWebPage();
	 * $web_page->res("css/sample.css");
	 * The function will search in : CLASS_WATCH_FOLDER/res/CLASS_RELATIVE_FOLDER/ClassName
	 * in this case:
	 * CLASS_WATCH_FOLDER = the framework path
	 * CLASS_RELATIVE_FOLDER = "web";
	 * ClassName = QWebPage
	 * 
	 * If this function determins that the files does not exist will try to find it for each of it's class parents
	 * for example if an QWebPage object can not find it it will try to find the file in the QWebControl's resource folder
	 *
	 * @param string $path the short path for the resource
	 * @param string $class_name the name of the class to search for
	 * @param boolean $check_parents if true will check the parents of the class for that resource
	 * @return string returns the full path for the resource
	 */
	public final function res($path, $class_name = null, $check_parents = true, $is_parent = false)
	{
		// we should map resources in the future to improve speed !
		if ($this->_use_default_res_path && (!$is_parent))
		{
			$res_file = rtrim($this->getDefaultResourcePath(), "\\/") . "/" . $path;
			if (file_exists($res_file))
				return $res_file;
		}
		
		if (!$class_name)
		{
			if ($this->_res_path)
			{
				$res_path = $this->_res_path.$path;
				if (file_exists($res_path))
					return $res_path;
			}
			
			$class_name = $this->getClassName();
		}

		if (!isset(QWebControl::$_ResPaths[$class_name]))
			$this->resPath($class_name, $is_parent);
		
		$res_path = QWebControl::$_ResPaths[$class_name].$path;

		if (file_exists($res_path))
			return $res_path;
		else if ($check_parents && (strtolower($class_name) != "qwebcontrol"))
			return $this->res($path, get_parent_class($class_name), $check_parents, true);
		else
			return null;
	}
	
	/**
	 * Same as QWebControl::res but it echo(es) the result
	 * Outputs the result of QWebControl::res
	 *
	 * @param string $path the short path for the resource
	 * @param string $class_name the name of the class to search for
	 * @param boolean $check_parents if true will check the parents of the class for that resource
	 */
	public final function r($path, $class_name = null, $check_parents = true)
	{
		echo $this->res($path, $class_name, $check_parents);
	}
	
	/**
	 * NOTE: We should map resources in the future to gain speed
	 * 
	 * Get the default resource path of the control
	 *
	 * @param string $class_name
	 * @return string
	 */
	public final function resPath($class_name = null, $is_parent = false)
	{
		if (!$class_name)
			$class_name = get_class($this);

		if (!isset(QWebControl::$_ResPaths[$class_name]))
		{
			if ($this->_use_default_res_path && (!$is_parent))
			{
				QWebControl::$_ResPaths[$class_name] = $this->getDefaultResourcePath();
			}
			else 
			{
				$res_path = QAutoload::GetClassWatchPath($class_name) . "res/";
				// strip php extension
				$res_path .= substr(QAutoload::GetRelativeClassPath($class_name), 0, - 4) . "/";
				
				QWebControl::$_ResPaths[$class_name] = QWebApp::RelativePath($res_path);
			}
		}
		
		return QWebControl::$_ResPaths[$class_name];
	}
	
	/**
	 * Sets an attribute to this control
	 *
	 * @param string $name
	 * @param string $value
	 */
	public final function setAttribute($name, $value)
	{
		if (!$this->_attributes)
			$this->_attributes = array();
			
		$this->_attributes[$name] = $value;
	}
	
	/**
	 * Gets an attribute of the control or null if not found
	 *
	 * @param string $name
	 * @return string
	 */
	public final function getAttribute($name)
	{
		return isset($this->_attributes[$name]) ? $this->_attributes[$name] : null;
	}
	
	/**
	 * Adds this control to another control
	 *
	 * @param QWebControl $control
	 */
	public final function addControl(QWebControl $control, $id = null)
	{
		if ($control instanceof QWebPage)
			throw new Exception("The control can not be a QWebPage");
		if ($id)
			$control->setId($id);		
		if (!$control->getId())
			throw new Exception("The control can not be added without an id/name");

		if (!isset($this->_controls))
		{
			$this->_controls = array();
			$this->_controls_index = array();
		}
		
		if (isset($control->_fullId))
			unset($control->_fullId);
			
		foreach ($this->_controls as $ctrl)
			if ($ctrl === $control)
				throw new Exception("The control already exists");

		if (!isset($this->_controls_index[$control->getId()]))
		{
			$this->_controls_index[$control->getId()] = $control;
			$this->_controls[] = $control;
			$control->_parent = $this;
		}
		else 
			throw new Exception("A control with the same ID already exists");
	}
	
	public final function removeAllControls()
	{
		if (isset($this->_controls))
		{
			$ctrl_count = count($this->_controls);
			for ($i = 0; $i < $ctrl_count; $i++)
			{
				$control = $this->_controls[$i];
				if (isset($control->_fullId))
					unset($control->_fullId);
				if ($control->getParent() === $this)
					$control->unsetParent();
			}
			unset($this->_controls_index);
			unset($this->_controls);
		}
	}
	
	/**
	 * Removes the specified control from the list 
	 * Returns true if found and removed, false if not found
	 *
	 * @param QWebControl $control
	 * @return boolean
	 */
	public final function removeControl(QWebControl $control)
	{
		if (!$this->_controls)
			return false;
		else
		{
			$ctrl_count = count($this->_controls);
			for ($i = 0; $i < $ctrl_count; $i++)
				if ($this->_controls[$i]->getId() == $control->getId())
				{
					if (isset($control->_fullId))
						unset($control->_fullId);
					if ($control->getParent() === $this)
						$control->unsetParent();
					if (isset($this->_controls_index[$control->getId()]))
						unset($this->_controls_index[$control->getId()]);
					$control->_parent = null;
					array_splice($this->_controls, $i, 1);
					return true;
				}
		}
		return false;
	}
	
	/**
	 * Gets the web page that owns this object or null if none is found
	 *
	 * @return QWebPage
	 */
	public final function getWebPage()
	{
		if ($this->isWebPage())
			return $this;
		if ($this->_parent)
			return $this->_parent->getWebPage();
		// throw new Exception("QWebControl::getWebPage() Not attached to a page");
		return null;
	}
	
	/**
	 * Return true if this control is a web page
	 *
	 * @return boolean
	 */
	public function isWebPage()
	{
		return false;
	}
	
	/**
	 * Adds a delegate to an event
	 *
	 * @param mixed $event_type
	 * @param QDelegate $delegate
	 */
	public final function addDelegate($event_type, QDelegate $delegate, $url_group = null, $trigger_time = QWebEvent::TriggerAfterLoad)
	{
		if (!$this->_events)
			$this->_events = array();
		if (!isset($this->_events[$event_type]))
		{
			if ($delegate instanceof QWebDelegate)
				$this->_events[$event_type] = new QWebEvent($event_type, array($delegate), $url_group, $trigger_time);
			else 
				$this->_events[$event_type] = new QEvent($event_type, array($delegate));
		}
		else 
		{
			// update the event with the new delegate
			if ($url_group)
				$this->_events[$event_type]->setUrlGroup($url_group);
			$this->_events[$event_type]->addDelegateByRef($delegate);
		}
	}
	
	public final function setEventTriggerTime($event_type, $trigger_time)
	{
		$this->_events[$event_type]->setTriggerTime($trigger_time);
	}
	
	public final function setEventUrlGroup($event_type, $url_group)
	{
		$this->_events[$event_type]->setUrlGroup($url_group);
	}
	
	/**
	 * Removes a delegate from an event
	 * Returns true if the delegate was found and removed
	 *
	 * @param mixed $event_type
	 * @param QDelegate $delegate
	 * @return boolean
	 */
	public final function removeDelegate($event_type, QDelegate $delegate)
	{
		if (!$this->_events)
			return false;
		if (!isset($this->_events[$event_type]))
			return false;
		return $this->_events[$event_type]->removeDelegate($delegate);
	}
	
	
	public final function removeAllDelegates($event_type)
	{
		if (!$this->_events)
			return false;
		if (!isset($this->_events[$event_type]))
			return false;
		$this->_events[$event_type]->removeAllDelegates();
		return true;
	}
	
	/**
	 * Renders the JS of the QWebPage
	 *
	 * @param QWebPage $web_page
	 * @param string $full_id The parent's fullId
	 * @param integer $depth The depth of this rendering step
	 */
	private final function renderJs(QWebPage $web_page, $full_id = null, $depth = 0, $parent_changed = false)
	{
		$tabs = str_pad("", $depth, "\t");
		$tabs_plus = $tabs."\t";
		
		if ($this->isWebPage() && (!$web_page->isAjaxRequest()))
		{
			if (QLanguages::GetLanguage() && QLanguages::$SendToJavaScript)
			{
				$web_page->jsAppend($tabs."window.QLanguages = {};\n");
				$web_page->jsAppend($tabs."window.QLanguages.language = ".json_encode(QLanguages::GetLanguage()).";\n");
				$web_page->jsAppend($tabs."window.QLanguages.data = ".json_encode(QLanguages::GetFullData()).";\n");
				$web_page->jsAppend($tabs."window.QLanguages.Get = function (key) {return this.data[key][this.language];};\n");
			}
		}
		
		$obj_name = "qjs_".$this->getId();
		
		if ($web_page->isAjaxRequest() && ($this->hasChanged() && (!$parent_changed)))
		{
			$web_page->jsAppend("\n{$tabs}var qjs_tmp_{$this->getId()} = GetQWebPage().getQWebControlById('{$full_id}');\n");
			$web_page->jsAppend("{$tabs}var qjs_tmp_parent_of_{$this->getId()} = qjs_tmp_{$this->getId()}.parent;\n");
			$web_page->jsAppend("{$tabs}qjs_tmp_parent_of_{$this->getId()}.doTempInit_{$obj_name} = function() \n{$tabs}{\n");
		}

		if ((!$web_page->isAjaxRequest()) || $parent_changed || $this->hasChanged())
		{

			if ($this->isWebPage())
			{
				$web_page->jsAppend("\n{$tabs}var $obj_name = new " . $this->getJsClass($web_page) . "();\n\n");
				$web_page->jsAppend("function FrameworkLoadPage()\n{\n");
				
			}
			else 
			{
				// $web_page->jsAppend("debugger;");
				$web_page->jsAppend($tabs_plus."var $obj_name = new " . $this->getJsClass($web_page) . "();\n");
			}
			
			if ($full_id)
			{
				$web_page->jsAppend($tabs_plus."$obj_name.fullId = \"$full_id\";\n");
				$web_page->jsAppend($tabs_plus."$obj_name.id = \"{$this->getId()}\";\n");
				$web_page->jsAppend($tabs_plus."$obj_name.domElement = document.getElementById(\"$full_id\");\n");
			}
			
			$web_page->jsAppend($tabs_plus."$obj_name.resPath = \"{$this->resPath()}\";\n");
			
			$this->doSendJsParams($web_page, $obj_name, $tabs_plus);
			
			if ($this->_parent)
			{
				$web_page->jsAppend($tabs_plus."this.addControl($obj_name);\n");
				/*$web_page->jsAppend($tabs_plus."$obj_name.parent = this;\n");
				
				if (!$this->isWebPage())
					$web_page->jsAppend($tabs_plus."$obj_name.root = GetQWebPage();\n");*/
			}
			
			if ($this->isWebPage())
			{
				$cs_index = 0;
				if ($this->getModule())
				{
					$web_page->jsAppend($tabs_plus."$obj_name.call_stack[$cs_index] = '".get_class($this->getModule())."';\n");
					$cs_index++;
				}
				$web_page->jsAppend($tabs_plus."$obj_name.call_stack[$cs_index] = '{$this->getClassName()}';\n");
				$web_page->jsAppend($tabs_plus."$obj_name.base_href = '{$this->getUrlBaseHref()}';\n");
				$web_page->jsAppend($tabs_plus."$obj_name.pathname = '{$this->getUrlPathname()}';\n");

				$web_page->jsAppend($tabs_plus."$obj_name.request_url = '{$this->getRequestUrl()}';\n");
				$web_page->jsAppend($tabs_plus."$obj_name.domElement = document.documentElement;\n");
				$web_page->jsAppend($tabs_plus."$obj_name.instance_id = '{$this->getInstanceId()}';\n");
			}
				
			$web_page->jsAppend($tabs_plus."if ($obj_name.domElement) $obj_name.domElement.jsCtrl = $obj_name;\n");
			
			/* RENDER DELEGATES */
			if ($this->_events)
			{
				foreach ($this->_events as $event_type => $event_obj)
				{
					$deleg_count = $event_obj->getDelegatesCount();
					for ($i = 0; $i < $deleg_count; $i++)
					{
						$delegate = $event_obj->getDelegateAt($i);
						// render delegate in the JS stream
						// addDelegate : function (event_type, delegate_type, callback_object_id, callback_method, params)
						if ($delegate instanceof QWebHrefDelegate)
						{
							// put a link to this control instead of javascript
						}
						else if ($delegate)
						{
							$web_page->jsAppend("{$tabs_plus}{$obj_name}.addDelegate('".QWebEvent::OnClick."', ".$delegate->toJsStream().");\n");
						}
					}
				}
			}
			// $web_page->jsAppend("alert('all ok');");
			
			/* END RENDER */
			
			if ($this->_controls)
			{
				$web_page->jsAppend("\n".$tabs_plus."$obj_name.doInitControls = function ()\n$tabs_plus{\n\t$tabs_plus".
					"this.controls = new Array();\n\n");
			}
		}
		else if ($web_page->isAjaxRequest())
		{
			$this->doSendJsParams($web_page, null, $tabs);
		}
	}

	/**
	 * Ends render the JS of the QWebPage
	 *
	 * @param QWebPage $web_page
	 * @param string $full_id The control's fullId
	 * @param integer $depth The depth of this rendering step
	 */
	protected final function renderJsEnd(QWebPage $web_page, $full_id = null, $depth = 0, $parent_changed = false)
	{
		$obj_name = "qjs_".$this->getId();
		$tabs = str_pad("", $depth, "\t");
		$tabs_plus = $tabs."\t";
		
		$init_called = false;

		if ((!$web_page->isAjaxRequest()) || $parent_changed || $this->hasChanged())
		{
			// not ajax request
			if ($this->_controls)
			{
				// $obj_name = "qjs_".$this->getId();

				$web_page->jsAppend("\n$tabs_plus}\n");
			}
			
			if ($this->isWebPage())
			{
				$init_called = true;

				$web_page->jsAppend("\n\t$obj_name.replaceSeoLinksWithAjax();\n\n");
				$web_page->jsAppend("\n\t$obj_name.initControls();\n\n");
				$web_page->jsAppend("\n\t$obj_name.triggerLoadControls();\n\n");
				
				if ($this->getEventCallbackJsData())
				{
					$web_page->jsAppend("\n{$tabs}GetQWebPage()._event_callback_js_data = ".json_encode($this->getEventCallbackJsData()).";\n");
					$web_page->jsAppend("{$tabs}GetQWebPage().manageEventCallbackJsData();\n");
				}
				
				$web_page->jsAppend("\n}\n");
				
				if (!$web_page->isAjaxRequest())
				{
					$web_page->jsAppend("\nfunction GetQWebPage()\n{\n\treturn qjs_{$web_page->getId()};\n}\n");
					$web_page->jsAppend("\nCrossBrowserAddEventHandler(window, \"load\", FrameworkLoadPage);\n\n");
				}
				else 
					$web_page->jsAppend("\nFrameworkLoadPage();\n\n");
			}
		}
		else 
		{
			if ($this->isWebPage() && $this->getEventCallbackJsData())
			{
				$web_page->jsAppend("\n{$tabs}GetQWebPage()._event_callback_js_data = ".json_encode($this->getEventCallbackJsData()).";\n");
				$web_page->jsAppend("{$tabs}GetQWebPage().manageEventCallbackJsData();\n");
			}
		}
		
		if ($web_page->isAjaxRequest() && ($this->hasChanged() && (!$parent_changed)))
		{
			// 
			if (!$init_called)
			{
				$web_page->jsAppend("\n{$tabs_plus}{$obj_name}.replaceSeoLinksWithAjax();\n");
				$web_page->jsAppend("\n{$tabs_plus}{$obj_name}.initControls();\n");
				$web_page->jsAppend("\n{$tabs_plus}{$obj_name}.triggerLoadControls();\n");
			}
			$web_page->jsAppend("{$tabs}}\n");
			
			$web_page->jsAppend("{$tabs}qjs_tmp_parent_of_{$this->getId()}.removeControl(qjs_tmp_{$this->getId()});\n");
			$web_page->jsAppend("{$tabs}qjs_tmp_parent_of_{$this->getId()}.doTempInit_{$obj_name}();\n");
		}
	}
	
	/**
	 * Gets the javascript class that this control should associate with
	 *
	 * @param QWebPage $web_page
	 * @return string
	 */
	public final function getJsClass(QWebPage $web_page)
	{
		if (!$this->_js_class)
			$this->_js_class = $web_page->getJsClassEquiv($this->getClassName());

		return $this->_js_class;
	}

	/**
	 * Gets the id of this control
	 *
	 * @return string
	 */
	public function getId()
	{
		return $this->_id;
	}
	
	/**
	 * Sets the id of this control
	 *
	 * @param string $id
	 */
	public function setId($id)
	{
		if ($this->_parent)
		{
			 if ($this->_parent->_controls_index[$this->_id])
			 	unset($this->_parent->_controls_index[$this->_id]);
			 	
			 $this->_parent->_controls_index[$id] = $this;
		}
		
		$this->_id = $id;
	}
	
	/**
	 * Returns true if this control has been marked as changed
	 *
	 * @return boolean
	 */
	public final function hasChanged()
	{
		return $this->_has_changed;
	}
	
	/**
	 * Sets the has_change value for this control
	 *
	 * @param boolean $value
	 */
	public final function setChanged($value)
	{
		$this->_has_changed = ($value == true);
	}

	/**
	 * Sets a viewstate variable to the object. The value can not be an object. Also you must be sure that the value has no recursive data within.
	 * Keys starting with '_' are reserved.
	 *
	 * @param string $key
	 * @param mixed $value
	 */
	public final function setViewstate($key, $value)
	{
		if (is_object($value) || is_resource($value))
			throw new Exception("Objects and resources are not allowed in the viewstate");
		if (is_array($value))
			foreach ($value as $k)
				if (is_object($k) || is_resource($k))
					throw new Exception("Objects and resources are not allowed in the viewstate");
		if (substr($key, 0, 1) == '_')
			throw new Exception("Keys starting with '_' in viewstate are reserved");

		$this->_viewstate[$key] = $value;
	}

	/**
	 * Gets the a viewstate variable based on a key.
	 * Keys starting with '_' are reserved.
	 *
	 * @param string $key
	 * @return mixed
	 */
	public final function getViewstate($key = null)
	{
		if (!$key)
			return $this->_viewstate;

		if (substr($key, 0, 1) == '_')
			throw new Exception("Keys starting with '_' in viewstate are reserved");

		if (isset($this->_viewstate[$key]))
			return $this->_viewstate[$key];
		else
			return null;
	}
	
	/**
	 * Resets the viewstate to null
	 *
	 */
	public final function resetViewstate()
	{
		unset($this->_viewstate);
		$this->_viewstate = null;
		if ($this->_parent && isset($this->_parent->_viewstate["_ctrls"]) && 
			isset($this->_parent->_viewstate["_ctrls"][$this->getId()]))
		{
			unset($this->_parent->_viewstate["_ctrls"][$this->getId()]);
		}
	}
	
	/**
	 * Unsets a variable in the viewstate based on the key.
	 * Keys starting with '_' are reserved.
	 *
	 * @param string $key
	 * @return mixed
	 */
	protected final function unsetViewstate($key)
	{
		if (substr($key, 0, 1) == '_')
			throw new Exception("Keys starting with '_' in viewstate are reserved");
			
		if (isset($this->_viewstate[$key]))
		{
			unset($this->_viewstate[$key]);
			return true;
		}
		else
			return false;
	}
	
	/**
	 * Gets the full id of this control
	 *
	 * @param string $parent_full_id The parent's full id to optimize this method
	 * @return string
	 */
	public function getFullId($parent_full_id = null)
	{
		if (isset($this->_fullId) && $this->_fullId)
			return $this->_fullId;
			
		if (!$this->getId())
			throw new Exception("QWebControl::getFullId() the control has no id");

		if (!$parent_full_id)
		{
			$parent = $this->getParent();
			if ($parent->isWebPage())
			{
				$this->_fullId = $this->getId();
				return $this->_fullId;
			}
			else 
				$parent_full_id = $parent->getFullId();
		}
		
		if ($this->getParent()->isWebPage())
			$this->_fullId = $this->getId();
		else 
			$this->_fullId = $parent_full_id . QWebControl::IdSeparator . $this->getId();

		return $this->_fullId;
	}
	
	public function getLink($type, $params = null)
	{
		return $this->getWebPage()->getUrlManager()->getLink($type, $params);
	}
	
	public function getUrlManager()
	{
		return $this->getWebPage()->getUrlManager();
	}
	
	public function getEventLink($event_type, $params = null, $merge_params = false)
	{
		if (!$this->hasEvent($event_type))
			throw new Exception("The event `$event_type` is not defined");

		return new QWebEventLink($this, $this->getEvent($event_type), $params, $merge_params);
	}
	
	public function printLink($type, $params = null)
	{
		echo $this->getLink($type, $params);
	}
	
	public function printEventLink($event_type, $params = null, $merge_params = false)
	{
		echo $this->getEventLink($event_type, $params, $merge_params);
	}
	
	/**
	 * Creates and triggers an event based on type and parameters
	 *
	 * @param mixed $type
	 * @param mixed $parameters
	 * @param boolean $web_event Set to true to create a QWebEvent instead of a QEvent
	 * 
	 * @return boolean True if the event was triggered
	 */
	public final function triggerEvent($type, $parameters, $web_event = false, $sender_full_id = null, $trigger_time = QWebEvent::TriggerAnyTime)
	{
		if ($this->hasEvent($type))
		{
			$ev = $this->_events[$type];
			if ($ev)
			{
				if (($trigger_time != QWebEvent::TriggerAnyTime) && ($ev->getTriggerTime() != $trigger_time))
					return false;

				$wp = $this->getWebPage();
				if ($wp && $wp->getUrlManager() && ($ev instanceof QWebEvent))
				{
					// $this->debug($sender_full_id, $type, $parameters);
					$wp->getUrlManager()->onTriggerEvent($wp, $sender_full_id, $ev, $parameters);
				}

				$ev->triggerEvent($this, $parameters);

				return true;
			}
			else 
			{
				throw new Exception("The event `{$type}` is not defined in: " . $this->getFullId());
			}
		}
		else 
			return false;
	}
	
	public final function hasEvent($type)
	{
		if (isset($this->_events) && isset($this->_events[$type]))
			return true;
		else 
			return false;
	}
	
	/**
	 * Sets the value for _use_default_res_path
	 * If true will use the default res path as set on QWebApp::$DefaultResPath
	 *
	 * @param boolean $value
	 */
	public final function setUseDefaultResPath($value)
	{
		$this->_use_default_res_path = $value;
	}
	
	/**
	 * Gets the value for use_default_res_path
	 * If true will use the default res path as set on QWebApp::$DefaultResPath
	 *
	 * @return boolean
	 */
	public final function getUseDefaultResPath()
	{
		return $this->_use_default_res_path;
	}
	
	/**
	 * Sets a parameter that will be sent to the javascript object 
	 * equivalent for this object
	 *
	 * @param string $key
	 * @param mixed $value Must be a scalar value
	 */
	public final function setJsParam($key, $value, $update_params = true)
	{
		if (!isset($this->_js_shared_params))
			$this->_js_shared_params = array();
		$this->_js_shared_params[$key] = $value;

		if ($update_params)
			$this->_js_shared_params_set = true;
	}
	
	/**
	 * Gets a parameter that will be sent to the javascript object 
	 * equivalent for this object
	 *
	 * @param string $key
	 * @return mixed A scalar value
	 */
	public final function getJsParam($key)
	{
		if (!isset($this->_js_shared_params))
			return null;
		if (isset($this->_js_shared_params[$key]))
			return $this->_js_shared_params[$key];
		else 
			return null;
	}
	
	/**
	 * Unsets a parameter that will be sent to the javascript object 
	 * equivalent for this object
	 *
	 * @param string $key
	 * @return boolean
	 */
	public final function unsetJsParam($key, $update_params = true)
	{
		// $this->_js_shared_params_set = $update_params;
		if (!isset($this->_js_shared_params))
			return false;
		if (isset($this->_js_shared_params[$key]))
		{
			$this->_js_shared_params[$key] = null;
			return true;
		}
		return false;
	}
	
	/**
	 * Clears all the parameters that should have been sent to the javascript object 
	 * equivalent for this object
	 *
	 */
	public final function clearJsParams()
	{
		unset($this->_js_shared_params);
	}
	
	/**
	 * Gets the inner content of the control if any was set
	 * if not will return null
	 *
	 * @return string
	 */
	public final function getInnerContent()
	{
		if (isset($this->_inner_content))
			return $this->_inner_content;
		else 
			return null;
	}
	
	/**
	 * Sets the inner HTML (or XML) of the control
	 * Once the inner content is set the template (if specified) will be ignored
	 *
	 * @param string $inner_content
	 */
	public final function setInnerContent($inner_content)
	{
		$this->_inner_content = $inner_content;
	}
	
	/**
	 * Unsets the inner content of the control
	 *
	 */
	public final function unsetInnerContent()
	{
		if ($this->_inner_content)
			unset($this->_inner_content);
	}
	
	/**
	 * Gets the parent of the control
	 *
	 * @return QWebControl
	 */
	public final function getParent()
	{
		if (isset($this->_parent))
			return $this->_parent;
		else 
			return null;
	}
	
	/**
	 * Sets the parent of the control
	 *
	 * @param QWebControl $parent
	 */
	public final function setParent(QWebControl $parent)
	{
		$this->_parent = $parent;
	}
	
	/**
	 * Unsets the parent of the control
	 *
	 */
	public final function unsetParent()
	{
		if (isset($this->_parent))
			unset($this->_parent);
	}
	
	/**
	 * Gets the template of the control if any was set
	 * Return null if it's not set
	 *
	 * @return string
	 */
	public final function getTemplate()
	{
		if (isset($this->_template))
			return $this->_template;
		else 
			return null;
	}
	
	/**
	 * Sets the template of the control
	 * If relative is set to true, then $this->res will be 
	 * called on the passed parameter . That means that setTemplate 
	 * expects the relative path by default
	 * If relative is changed to false then the path is exactly the specified path
	 *
	 * @param string $template
	 * @param boolean $relative
	 */
	public final function setTemplate($template, $relative = true)
	{
		if ($relative)
			$this->_template = $this->res($template);
		else 
			$this->_template = $template;
	}
	
	/**
	 * Unsets a template
	 *
	 */
	public final function unsetTemplate()
	{
		if (isset($this->_template))
			unset($this->_template);
	}
	
	/**
	 * Adds a resource to the web control
	 * The resource will be included by the web page control
	 * The $relative parameter is set to true by default, that 
	 * means addResource expects the path to be relative to the 
	 * resource folder of the control
	 * If you have a different path set $relative to false
	 * For CSS files set $type to "css"
	 * For JavaScript files set $type to "js"
	 *
	 * @param string $path
	 * @param mixed $type
	 * @param array $attrs Example : array("media" => "screen")
	 * @param boolean $relative
	 */
	public final function addResource($path, $type, $attrs = null, $relative = true)
	{
		if ($relative)
			$t_path = $this->res($path);
		else 
			$t_path = $path;
		
		if (strlen(trim($t_path)) == 0)
			throw new Exception("File can not be empty or not found: {$t_path} | {$path}");

		if (!isset($this->_resources))
			$this->_resources = array();

		$this->_resources[$t_path] = array("type" => $type, "attrs" => $attrs);
	}

	/**
	 * Removes a resource from the control. 
	 * Please see addResource to understand the use
	 * Returns true if the path was found in the control's list 
	 * for the specifed type, false if not
	 *
	 * @param string $path
	 * @param mixed $type
	 * @param boolean $relative
	 * @return boolean
	 */
	public final function removeResource($path, $relative = true)
	{
		if ($relative)
			$t_path = $this->res($path);
		else 
			$t_path = $path;
		
		if (!isset($this->_resources))
			return false;

		if (isset($this->_resources[$t_path]))
		{
			unset($this->_resources[$t_path]);
			return false;
		}
		else 
			return false;
	}
	
	/**
	 * Determines if the control has the specified resource
	 * 
	 * @param string $path
	 * @param mixed $type
	 * @param boolean $relative
	 * @return boolean
	 */
	public final function hasResource($path, $relative = true)
	{
		if ($relative)
			$t_path = $this->res($path);
		else 
			$t_path = $path;
		
		if (!isset($this->_resources))
			return false;
		if (isset($this->_resources[$t_path]))
			return true;
		else 
			return false;
	}
	
	/**
	 * Add a Javascript resource to the control
	 *
	 * @param string $path
	 * @param array $attrs Example : array("media" => "screen")
	 * @param boolean $relative
	 */
	public final function addJs($path, $attrs = null, $relative = true)
	{
		// var_dump($path);
		$this->addResource($path, "js", $attrs, $relative);
	}
	
	/**
	 * Add a CSS resource to the control
	 *
	 * @param string $path
	 * @param array $attrs Example : array("media" => "screen")
	 * @param boolean $relative
	 */
	public final function addCss($path, $attrs = null, $relative = true)
	{
		$this->addResource($path, "css", $attrs, $relative);
	}
	
	/**
	 * Remove a Javascript resource
	 * Returns true if the file was found and removed from the list
	 *
	 * @param string $path
	 * @param boolean $relative
	 * @return boolean
	 */
	public final function removeJs($path, $relative = true)
	{
		return $this->removeResource($path, $relative);
	}
	
	/**
	 * Removes a CSS resource from the control
	 * Returns true if the file was found and removed from the list
	 *
	 * @param string $path
	 * @param boolean $relative
	 * @return boolean
	 */
	public final function removeCss($path, $relative = true)
	{
		return $this->removeResource($path, $relative);
	}
	
	/**
	 * Returns true if the control has the specified Javascript resource
	 *
	 * @param string $path
	 * @param boolean $relative
	 * @return boolean
	 */
	public final function hasJs($path, $relative = true)
	{
		return $this->hasResource($path, $relative);
	}
	
	/**
	 * Returns true if the control has the specified CSS resource
	 *
	 * @param string $path
	 * @param boolean $relative
	 * @return boolean
	 */
	public final function hasCss($path, $relative = true)
	{
		return $this->hasResource($path, $relative);
	}
	
	/**
	 * Gets the resources (and the data) of this control
	 *
	 * @return array
	 */
	protected final function getResourcesData()
	{
		if (isset($this->_resources))
			return $this->_resources;
		else 
			return null;
	}
	
	/**
	 * Gets an element based on it's fullId
	 *
	 * @param string $id The full id of the element
	 * @return QWebControl
	 */
	public final function getElementById($id)
	{
		$parts = explode(QWebControl::IdSeparator, $id);
		$itm = $this;
		$last = count($parts) - 1;

		$i = 0;
		if ($parts[0] == $this->getId())
		{
			if ($last == 0)
				return $this;
			$i++;
		}

		for (; $i <= $last; $i++)
		{
			$p = $parts[$i];
			if (isset($itm->_controls_index[$p]))
			{
				$itm = $itm->_controls_index[$p];
				if ($i == $last)
				{
					return $itm;
				}
			}
			else 
				return null;
		}
		return null;
	}
	
	/**
	 * Adds a execute chunk to the ajax chunks list
	 *
	 * @param string $elemnt_id The id of the element to be replaced
	 * @param string $html_for_replace The html that will replace the element
	 * @return integer The position where the chunk was inserted
	 */
	public final function executeJs($js_as_text)
	{
		return $this->getWebPage()->addAjaxExecuteChunk($js_as_text);
	}
	
	public function setEventDefaultParameters($event_type, $default_params)
	{
		if (isset($this->_events[$event_type]))
			$this->_events[$event_type]->setDefaultParameters($default_params);
		else 
			throw new Exception("The event ({$event_type}) does not exists in this control");
	}
	
	/**
	 * Gets the event by type if any
	 *
	 * @param string $type
	 * @return QEvent
	 */
	public function getEvent($type)
	{
		if (isset($this->_events[$type]))
			return $this->_events[$type];
		else 
			return null;
	}
	
	public function getEventParameter($type, $param)
	{
		if (isset($this->_events[$type]))
			return $this->_events[$type]->getParameter($param);
		else 
			return null;
	}
	
	/**
	 * Gets the default resource path for QWebControl(s)
	 *
	 * @return string
	 */
	public function getDefaultResourcePath()
	{
		if (isset($this->_res_path) && $this->_res_path)
			return $this->_res_path;
		if ($this instanceof QWebPage)
			$module = $this->getModule();
		else 
		{
			$page = $this->getWebPage();
			if (!$page)
				$page = QWebPage::GetLoadedPage();
			if (!$page)
				throw new Exception("The control is not attached to a QWebPage");
			$module = $page->getModule();
		}
		if (!$module)
			throw new Exception("The WebPage is not attached to a QWebModule");
			
		$this->_res_path = $module->getDefaultResourcePath();
		return $this->_res_path;
	}
	
	public function getJsCtrl($quote = "'")
	{
		if ($this->isWebPage())
			return "GetQWebPage()";
		else
			return "GetQWebPage().getQWebControlById({$quote}".$this->getFullId()."{$quote})";
	}
	
	public function getJsEventStr($quote = "'", $event_type, $params = null)
	{
		if ($this->isWebPage())
			return "GetQWebPage().triggerEvent({$quote}{$event_type}{$quote}, ".json_encode($params).")";
		else 
			return "GetQWebPage().getQWebControlById({$quote}".$this->getFullId()."{$quote}).triggerEvent({$quote}{$event_type}{$quote}, ".json_encode($params).")";
	}
	
	public function setRenderDelegate($delegate)
	{
		$this->_render_delegate = $delegate;
	}
	
	public function getRenderDelegate()
	{
		if (isset($this->_render_delegate))
			return $this->_render_delegate;
		else 
			return null;
	}
	
	/**
	 * Sets a property of this web control to be saved and restored 
	 * to the viewstate and from the viewstate automatically
	 *
	 * @param string $property_name
	 */
	public function setViewstateManagedProperty($property_name)
	{
		if (!isset($this->_viewstate_managed_properties))
			$this->_viewstate_managed_properties = array();
		if (!in_array($property_name, $this->_viewstate_managed_properties))
			$this->_viewstate_managed_properties[] = $property_name;
	}
	
	/**
	 * Unsets a property from the list of properties automatically managed by the viewstate
	 *
	 * @param string $property_name
	 * @return boolean Returns true if the property was found in the list and removed
	 */
	public function unsetViewstateProperty($property_name)
	{
		if (!isset($this->_viewstate_managed_properties))
			return false;
		if (!isset($this->_viewstate_managed_properties[$property_name]))
			return false;
		
		unset($this->_viewstate_managed_properties[$property_name]);
		return true;
	}
	
	/**
	 * Sets a property of this web control to be send and retreived (if the js modifies it)
	 * to the javascript object automatically
	 * THE WAY BACK IS NOT IMPLEMENTED YET, VALUES WILL NOT BE SENT BACK FROM JAVASCRIPT TO PHP !!!
	 *
	 * @param string $property_name
	 */
	public function setJsSyncedProperty($property_name)
	{
		if (!isset($this->_js_sync_properties))
			$this->_js_sync_properties = array();
		if (!in_array($property_name, $this->_js_sync_properties))
			$this->_js_sync_properties[] = $property_name;
	}
	
	/**
	 * Unsets a property of this web control from beeing sent and retreived (if the js modifies it)
	 * to the javascript object automatically
	 * THE WAY BACK IS NOT IMPLEMENTED YET, VALUES WILL NOT BE SENT BACK FROM JAVASCRIPT TO PHP !!!
	 *
	 * @param string $property_name
	 * @return boolean Returns true if the property was found in the list and removed
	 */
	public function unsetJsSyncedProperty($property_name)
	{
		if (!isset($this->_js_sync_properties))
			return false;
		if (!isset($this->_js_sync_properties[$property_name]))
			return false;
		
		unset($this->_js_sync_properties[$property_name]);
		return true;
	}
	
	/**
	 * Sends the javascript shared parameters to javascript
	 * THE WAY BACK IS NOT IMPLEMENTED YET, VALUES WILL NOT BE SENT BACK FROM JAVASCRIPT TO PHP !!!
	 *
	 * @param QWebPage $web_page
	 * @param string $obj_name The name of the javascript object to be called, if null will be called by ID
	 * @param integer $tabs The number of tabs to use to arrange the output
	 */
	protected function doSendJsParams(QWebPage $web_page, $obj_name = null, $tabs = 1)
	{
		if (!$this->_js_shared_params_set)
			return ;

		if (isset($this->_js_sync_properties))
		{
			foreach ($this->_js_sync_properties as $prop)
				if (isset($this->{$prop}))
					$this->setJsParam($prop, $this->{$prop});
		}

		if (isset($this->_js_shared_params) && (count($this->_js_shared_params) > 0))
		{
			if (!$obj_name)
			{
				$full_id = $this->getFullId();
				if ($this->isWebPage())
					$web_page->jsAppend("\n{$tabs}GetQWebPage()._setJsSharedParams( ".json_encode($this->_js_shared_params).", true );\n");
				else 
					$web_page->jsAppend("\n{$tabs}GetQWebPage().getQWebControlById('{$full_id}')._setJsSharedParams( ".json_encode($this->_js_shared_params).", true );\n");
			}
			else 
			{	
				$web_page->jsAppend($tabs."$obj_name._setJsSharedParams( ".json_encode($this->_js_shared_params)." , false );\n");
			}
		}
	}
	
	/**
	 * Loads the control into it's context
	 *
	 * @param QWebPage $web_page
	 * @param boolean $call_init If true init will be called
	 * @param boolean $trigger_events If true events will be triggered
	 * @param boolean $load_viewstate If true the viewstate will be loaded
	 * @param boolean $load_postback If true the postback data will be loaded
	 * @param boolean $call_load If true load will be called 
	 * @param boolean $call_after_events If true doAfterEvents will be called
	 */
	public function doLoadControl(QWebPage $web_page, $call_init = true, 
		$trigger_events = false, $load_viewstate = true, $load_postback = true, $call_load = true, 
		$call_after_events = false)
	{
		if ($call_init)
			$this->doInit();
			
		$trigger_for = null;
		if ($trigger_events)
			$trigger_for = ($this === $web_page) ? null : $this;

		// TriggerAfterInit ???
		if ($trigger_events)
			$web_page->triggerRequestEvents(QWebEvent::TriggerAfterInit, $trigger_for);
		
		if ($web_page->isPostback())
		{
			// load viewstate
			if ($load_viewstate)
			{
				// if ($this->_w)
				$this->_viewstate = $web_page->getViewstateData($this);
				$this->doLoadViewstate($web_page);
			}
			// load postback data
			
			/* never used !
			if ($load_postback)
				$this->doLoadPostback($web_page);
			*/
		}

		// TriggerBeforeLoad ??? 
		if ($trigger_events)
			$web_page->triggerRequestEvents(QWebEvent::TriggerBeforeLoad, $trigger_for);
		// call onload event
		if ($call_load)
			$this->doLoad($web_page);

		// trigger events TriggerAfterLoad
		if ($trigger_events)
			$web_page->triggerRequestEvents(QWebEvent::TriggerAfterLoad, $trigger_for);

		if ($this === $web_page)
			$web_page->setHasTriggeredEvents(true);

		if ($call_after_events)
			$this->doAfterEvents($this->getWebPage());
	}
	
	/**
	 * Reloads the control by calling doInit and doLoad
	 * between the 2 calls the control is added to the specified parent
	 * 
	 * @param QWebControl $parent 
	 */
	public final function reloadControl(QWebControl $parent = null, 
		$load_viewstate = true, $load_postback = true, $call_do_load = true)
	{
		if ($this->isWebPage())
			throw new Exception("The control to reload can not be a web page");
		if ($parent)
			$parent->addControl($this);
		else 
			$parent = $this->getParent();
		
		$wp = $this->getWebPage();
		$this->doLoadControl($wp, true, false, $load_viewstate, $load_postback, $call_do_load);
		/*
		$this->doInit();

		
		if ($parent && $load_viewstate && isset($parent->_viewstate["_ctrls"]) && 
				isset($parent->_viewstate["_ctrls"][$this->_id]))
		{
			$this->_viewstate = $parent->_viewstate["_ctrls"][$this->_id];			
			$this->doLoadViewstate($this->getWebPage());
		}
		
		if ($load_postback && $wp && $parent)
			$this->doLoadPostback();

		if ($call_do_load)
			$this->doLoad($this->getWebPage());
		*/
	}
	
	public function getPageLink($page_class)
	{
		return $this->getWebPage()->getPageLink($page_class);
	}
	
	public function navigateTo($page_class)
	{
		$this->getWebPage()->navigateTo($page_class);
	}
	
	public function setJsSharedParamsSet($value)
	{
		$this->_js_shared_params_set = $value;
	}
	
	public function getJsSharedParamsSet()
	{
		return $this->_js_shared_params_set;
	}
}

?>